package cn.spy.threaddownload.service;

import cn.spy.threaddownload.exception.CustomException;
import cn.spy.threaddownload.model.DownloadConfig;
import cn.spy.threaddownload.thread.DownloadTask;
import cn.spy.threaddownload.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 文件操作服务实现
 * @author sunpeiyu
 * @date 2023-04-10
 */
@Slf4j
@Component
public class DownloadServiceImpl implements IDownloadService {
    @Autowired
    private DownloadConfig config;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    private CountDownLatch countDownLatch = null;

    @PostConstruct
    public void initCdl() {
        countDownLatch = new CountDownLatch(config.getFilePartNum());
    }

    @Override
    public String downloadFile(String url) throws IOException {
        // 临时目录的路径
        String tempDir = config.getTempDir();
        // 文件分成的段数
        int filePartSum = config.getFilePartNum();
        // 获取连接
        HttpURLConnection connection = HttpUtils.getUrlConnection(url);
        connection.setConnectTimeout(10000);
        connection.setRequestMethod("GET");
        // 文件名
        int index = url.lastIndexOf("/");
        String fileName = url.substring(index + 1);
        // 文件的大小
        int contentLength = connection.getContentLength();
        // 单个文件的大小
        int partFileSize = contentLength / filePartSum;
        connection.disconnect();

        for (int i = 0 ; i < filePartSum ; i++) {
            DownloadTask task = new DownloadTask(
                    countDownLatch, url,
                    partFileSize, contentLength,
                    i, filePartSum, tempDir
            );

            threadPoolExecutor.execute(task);
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new CustomException(e.getMessage());
        }

        log.info(">>>>>>>>>>>>>>>>>>>> download success <<<<<<<<<<<<");
        this.mergeFile(fileName, contentLength);
        log.info(">>>>>>>>>>>>>>>>>>>> merge success <<<<<<<<<<<<");
        // 删除临时文件
        // this.deleteTempFile();
        return config.getOutFileDir() + File.separator + fileName;
    }

    private void mergeFile(String fileName, int contentLength) throws IOException {
        String tempDir = config.getTempDir();
        File file = new File(tempDir);

        if (!file.exists() || !file.isDirectory()) {
            return;
        }

        if (countDownLatch.getCount() != 0) {
            return;
        }

        List<File> list = Arrays.asList(Objects.requireNonNull(file.listFiles()));
        List<File> sortedList = list.stream().
                sorted(Comparator.comparing(innerFile -> Integer.parseInt(innerFile.getName()))).collect(Collectors.toList());
        String outFileDir = config.getOutFileDir();
        int dataPos = 0;
        RandomAccessFile randomAccessFile =
                new RandomAccessFile(outFileDir + File.separator + fileName, "rw");
        for (File partFile : sortedList) {
            randomAccessFile.seek(dataPos);
            FileInputStream fis = new FileInputStream(partFile);
            byte[] arr = new byte[contentLength];
            int len = 0;
            while ((len = fis.read(arr)) != -1) {
                randomAccessFile.write(arr, 0, len);
                dataPos += len;
            }
        }

        randomAccessFile.close();
    }

    private void deleteTempFile() {
        String tempDir = config.getTempDir();
        File file = new File(tempDir);

        if (!file.exists()) {
            throw new CustomException("文件夹不存在！");
        }

        if (!file.isDirectory()) {
            throw new CustomException("该文件不是文件夹！");
        }

        try {
            FileUtils.cleanDirectory(file);
            FileUtils.deleteDirectory(file);
        } catch (IOException e) {
            throw new CustomException(e.getMessage());
        }
    }

}
